#include "Cr.h"

namespace cr
{
	class InternalCrcManager
	{
	public:

					InternalCrcManager()	{ Initialise(); }
		virtual		~InternalCrcManager()	{}

		void		GeneratePartialCrc(uint32_t *ulCRC, const uint8_t* sData, size_t ulDataLength)
		{
			while(ulDataLength--)
			{
				//If your compiler complains about the following line, try changing each
				//    occurrence of *ulCRC with "((UInt32)*ulCRC)" or "*(UInt32 *)ulCRC".

				*(uint32_t *)ulCRC =
					((*(uint32_t *)ulCRC) >> 8) ^ ulTable[((*(uint32_t *)ulCRC) & 0xFF) ^ *sData++];
			}
		}

		uint32_t	GenerateCrc(const uint8_t* sData, size_t ulDataLength)
		{
			uint32_t ulCRC = 0xffffffff; //Initilaize the CRC.
			GeneratePartialCrc(&ulCRC, sData, ulDataLength);
			return(ulCRC ^ 0xffffffff); //Finalize the CRC and return.
		}

	private:

		uint32_t	ulTable[256]; // CRC lookup table array.

		void		Initialise(void)
		{
			//0x04C11DB7 is the official polynomial used by PKZip, WinZip and Ethernet.
			uint32_t ulPolynomial = 0x04C11DB7;

			// 256 values representing ASCII character codes.
			for(int iCodes = 0; iCodes <= 0xFF; iCodes++)
			{
				ulTable[iCodes] = Reflect(iCodes, 8) << 24;

				for(int iPos = 0; iPos < 8; iPos++)
				{
					ulTable[iCodes] = (ulTable[iCodes] << 1)
						^ ((ulTable[iCodes] & (1 << 31)) ? ulPolynomial : 0);
				}

				ulTable[iCodes] = Reflect(ulTable[iCodes], 32);
			}
		}

		uint32_t	Reflect(uint32_t ulReflect, const char cChar)
		{
			uint32_t ulValue = 0;

			// Swap bit 0 for bit 7, bit 1 For bit 6, etc....
			for(int iPos = 1; iPos < (cChar + 1); iPos++)
			{
				if(ulReflect & 1)
				{
					ulValue |= (1 << (cChar - iPos));
				}
				ulReflect >>= 1;
			}

			return ulValue;
		}
	};

	static InternalCrcManager	g_internalCrcManager;

	uint32_t	crc32(const uint8_t* pData, size_t dataSize)
	{
		return g_internalCrcManager.GenerateCrc(pData, dataSize);
	}

	uint32_t	crc32(const char*	 szData)
	{
		return crc32((const uint8_t*)szData, strlen(szData));
	}
}